package repositories

import (
	"context"
	"github.com/pkg/errors"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"gorm.io/gorm"
)

type ApiRepository interface {
	Create(ctx context.Context, apiInfo *models.Api) (*models.Api, error)
	FindAll(ctx context.Context) ([]*models.Api, error)
	FindByAppID(ctx context.Context, appID uint64) (result []*models.Api, err error)
	FindByAppName(ctx context.Context, appName string) (result []*models.Api, err error)
	Find(ctx context.Context, appName, query string, kind int) ([]*models.Api, error)
	Update(ctx context.Context, apiInfo *models.Api) error
	UpdateByAppName(ctx context.Context, appName, Owner string, level int) error
	DeleteByID(ctx context.Context, ID uint64) error
	DeleteByIDs(ctx context.Context, IDs []uint64) error
	FindByID(ctx context.Context, ID uint64) (*models.Api, error)
	FindByZeus(ctx context.Context, appName, zeusService, zeusMethod string, kind int) (*models.Api, error)
	FindByQuery(ctx context.Context, query string, pageNum int, pageSize int) (list []*models.Api, count int64, err error)
	FindByHttpPath(ctx context.Context, appName, httpMethod, httpPath string) (*models.Api, error)
	FindByDisableGraph(ctx context.Context) ([]*models.Api, error)
	//查询zeus下客户端
	FindByZeusClient(ctx context.Context, zeusMethod, zeusService string) ([]*models.Api, error)
	//查询zeus下服务端
	FindByZeusServer(ctx context.Context, zeusMethod, zeusService string) (*models.Api, error)

	//清理重复数据
	FindRepeat(ctx context.Context) ([]*models.Api, error)
}

type apiRepository struct {
	db *gorm.DB
}

func NewApiInfoRepository(db *gorm.DB) ApiRepository {
	return &apiRepository{
		db: db,
	}
}

func (a *apiRepository) Create(ctx context.Context, apiInfo *models.Api) (*models.Api, error) {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Create(apiInfo).Error; err != nil {
		return nil, errors.Wrap(err, "create apiInfo error")
	}
	return apiInfo, nil
}
func (a *apiRepository) Find(ctx context.Context, appName, query string, kind int) ([]*models.Api, error) {
	result := make([]*models.Api, 0)
	db := a.db.WithContext(ctx).Table(models.ApiInfoTableName)
	if appName != "" {
		db.Where("app_name like ? ", "%"+appName+"%")
	}
	if query != "" {
		if kind == 0 {
			db.Where("http_path like ? and `type`=?", "%"+query+"%", kind)
		} else {
			db.Where("zeus_method like ? and `type`=?", "%"+query+"%", kind)
		}
	}
	if err := db.Find(&result).Error; err != nil {
		return nil, errors.Wrap(err, "find  apiInfo error")
	}
	return result, nil
}

func (a *apiRepository) FindAll(ctx context.Context) (result []*models.Api, err error) {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Find(&result).Error; err != nil {
		return nil, errors.Wrap(err, "find all apiInfo error")
	}
	return result, nil
}

func (a *apiRepository) FindByAppName(ctx context.Context, appName string) (result []*models.Api, err error) {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Find(&result, "app_name=?", appName).Error; err != nil {
		return nil, errors.Wrap(err, "find all apiInfo error")
	}
	return result, nil
}

func (a *apiRepository) FindByAppID(ctx context.Context, appID uint64) (result []*models.Api, err error) {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Find(&result, "app_id=?", appID).Error; err != nil {
		return nil, errors.Wrap(err, "find all apiInfo error")
	}
	return result, nil
}

func (a *apiRepository) Update(ctx context.Context, apiInfo *models.Api) error {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Save(apiInfo).Error; err != nil {
		return errors.Wrap(err, "update apiInfo error")
	}
	return nil
}
func (a *apiRepository) UpdateByAppName(ctx context.Context, appName, Owner string, level int) error {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Where("app_name =?", appName).Updates(map[string]interface{}{"owner": Owner, "level": level}).Error; err != nil {
		return errors.Wrap(err, "update apiInfo error")
	}
	return nil
}

func (a *apiRepository) DeleteByID(ctx context.Context, ID uint64) error {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Delete(&models.Api{}, "id = ?", ID).Error; err != nil {
		return errors.Wrap(err, "delete apiInfo error")
	}
	return nil
}

func (a *apiRepository) DeleteByIDs(ctx context.Context, IDs []uint64) error {
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Delete(&models.Api{}, IDs).Error; err != nil {
		return errors.Wrap(err, "delete apiInfo error")
	}
	return nil
}

func (a *apiRepository) FindByID(ctx context.Context, ID uint64) (*models.Api, error) {
	var apiInfo = new(models.Api)
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Where("id = ?", ID).First(&apiInfo).Error; err != nil {
		return nil, errors.Wrap(err, "find apiInfo by id error")
	}

	return apiInfo, nil
}
func (a *apiRepository) FindByHttpPath(ctx context.Context, appName, httpMethod, httpPath string) (*models.Api, error) {
	var apiInfo = new(models.Api)
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Where("app_name = ? and http_method=? and http_path=? and `type`=0", appName, httpMethod, httpPath).First(&apiInfo).Error; err != nil {
		return nil, errors.Wrap(err, "find apiInfo by id error")
	}
	return apiInfo, nil
}

func (a *apiRepository) FindByZeus(ctx context.Context, appName, zeusService, zeusMethod string, kind int) (*models.Api, error) {
	var apiInfo = new(models.Api)
	db := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Where("app_name = ? and zeus_method=? and zeus_service=?", appName, zeusMethod, zeusService)
	if kind < 3 {
		db = db.Where("`type`=?", kind)
	}
	if err := db.First(&apiInfo).Error; err != nil {
		return nil, errors.Wrap(err, "find apiInfo by id error")
	}
	return apiInfo, nil
}

func (a *apiRepository) FindByQuery(ctx context.Context, query string, pageNum int, pageSize int) (list []*models.Api, count int64, err error) {
	db := a.db.Table(models.ApiInfoTableName)
	if len(query) > 0 {
		db = db.Where("zeus_method like ? or http_path like ?", "%"+query+"%", "%"+query+"%")
	}
	if err := db.Count(&count).Limit(pageSize).Offset((pageNum - 1) * pageSize).Scan(&list).Error; err != nil {
		return nil, 0, errors.Wrap(err, "find api info error")
	}
	return list, count, nil
}
func (a *apiRepository) FindByDisableGraph(ctx context.Context) ([]*models.Api, error) {
	result := make([]*models.Api, 0)
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Find(&result, "disable_graph->>'$.graph_id' is not null").Error; err != nil {
		return nil, errors.Wrap(err, "find all apiInfo error")
	}
	return result, nil
}
func (a *apiRepository) FindRepeat(ctx context.Context) ([]*models.Api, error) {
	result := make([]*models.Api, 0)
	if err := a.db.WithContext(ctx).
		Raw("select * from t_api where id not  in (select min(id) from t_api group by app_name,level,http_method,http_path,zeus_method,zeus_service,`type`)").
		Scan(&result).Error; err != nil {
		return nil, errors.Wrap(err, "find all apiInfo error")
	}
	return result, nil
}

func (a *apiRepository) FindByZeusClient(ctx context.Context, zeusMethod, zeusService string) ([]*models.Api, error) {
	result := make([]*models.Api, 0)
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).Find(&result, "zeus_method =? and zeus_service=? and `type`=2", zeusMethod, zeusService).Error; err != nil {
		return nil, errors.Wrap(err, "find FindByZeusClient error")
	}
	return result, nil
}

func (a *apiRepository) FindByZeusServer(ctx context.Context, zeusMethod, zeusService string) (*models.Api, error) {
	result := new(models.Api)
	if err := a.db.WithContext(ctx).Table(models.ApiInfoTableName).First(&result, "zeus_method =? and zeus_service=? and `type`=1", zeusMethod, zeusService).Error; err != nil {
		return nil, errors.Wrap(err, "find FindByZeusClient error")
	}
	return result, nil
}
